
package bestTeam.WalkieDoggie.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import bestTeam.WalkieDoggie.client.Facebook;
import bestTeam.WalkieDoggie.client.Location;
import bestTeam.WalkieDoggie.client.NotLoggedInException;
import bestTeam.WalkieDoggie.client.Serializables.FBFriend;
import bestTeam.WalkieDoggie.client.Serializables.LoginInfo;
import bestTeam.WalkieDoggie.client.Serializables.UserProfileData;
import bestTeam.WalkieDoggie.client.ServicesInterfaces.FacebookUsersService;
import bestTeam.WalkieDoggie.server.Persistant.AppUser;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.restfb.Connection;
import com.restfb.DefaultFacebookClient;
import com.restfb.FacebookClient;
import com.restfb.Parameter;
import com.restfb.types.User;

public class FacebookUsersServiceImpl extends RemoteServiceServlet implements FacebookUsersService {

	private static final PersistenceManagerFactory PMF = PersistanceManager.getPMF();


	//shira

	//		private static final String access_token = "AAACAezrPW3cBAAZA3sRIhrp80Wy8tclR9auDJfb7yw70anjZBIOhNrgDgYkseECZAbP6Wv59c6hf52KFuVbtZAGzVYfCFNZAFu7FqTl3qNQZDZD";

	//	private static final String access_token = "AAACAezrPW3cBAAZA3sRIhrp80Wy8tclR9auDJfb7yw70anjZBIOhNrgDgYkseECZAbP6Wv59c6hf52KFuVbtZAGzVYfCFNZAFu7FqTl3qNQZDZD";


	//jimmy
//				private static final String access_token = "AAACAezrPW3cBAPPeLogsaQGxXOesZCWabfvpVqDvTpUZBZAANQYZCatKEs7MZBXTtQ3eYAsUn4TxIiZAwBAaCvQZAdvuWEwxlTOWOkCZAmetZBwZDZD";

	//	yaara
	//					private static final String access_token = "AAACAezrPW3cBAA1619rKZA6kFsguedaCkRZBTnMhi8keTElFWosAgr9Lk6jqxrrkhZC8DOKMVHSxE366Xu7DsrUGLXLCbwXRZCfbHcagKgZDZD";

	//omer
//				private static final String access_token = "AAACAezrPW3cBANbzLbq50oDjbTmhayxZC5DxqCbjjTVkn0vW1k8O1zioXV21hHUgp1LW51Pm8X7LZCGSQWpOrR55tCTRb7wRxeXO5lEgZDZD";
	//shachaf
		//			private static final String access_token = "AAACAezrPW3cBANensvs9ucJ8C1XAmboQbziXCZAeayT3ZAWofZBDWbUBAgoRAwxPZBYsupPT3jYsbuXbQM2f3K9z0ZCdz8RjNxgOZC12x8EwZDZD";


	//ofer
		private static final String access_token = "AAACAezrPW3cBAJ2wYeIwap9HYXOq0tZCfz27qOWXG7fZBURn4DW9mTsedAVPFRZCFAbQdtYfwJxe0AnaYngU7Swm2RQ6perwP0AwxHYZBQZDZD";

	@Override
	public UserProfileData[] getUsers(String[] usersId){
		//checkLoggedIn();
		PersistenceManager pm = PMF.getPersistenceManager();
		List<UserProfileData> res = new ArrayList<UserProfileData>();
		try {

			Query q = pm.newQuery(AppUser.class);
			q.declareParameters("String id");

			for (String id : usersId) {

				q.setFilter("uid == id");

				List<AppUser> appUsers = (List<AppUser>) q.execute(id);

				if(appUsers==null)
					continue;

				if(appUsers.isEmpty()){
					continue;
				}


				for (AppUser appUser: appUsers) {

					UserProfileData volunteer = appUserToUserProfileData(appUser);
					res.add(volunteer);

				}
			}


		} finally {
			pm.close();
		}

		return (UserProfileData[]) res.toArray(new UserProfileData[0]);
	}

	@Override
	public void setCredits(String id, Integer delta) throws NotLoggedInException {

		//checkLoggedIn();
		PersistenceManager pm = PMF.getPersistenceManager();
		try {
			Query q = pm.newQuery(AppUser.class, " uid == id");
			q.declareParameters("String id");

			List<AppUser> users = (List<AppUser>) q.execute(id);

			pm.currentTransaction().begin();

			//Subtract one credit from current user
			if(users!=null && !users.isEmpty()){

				for (AppUser appUser : users) {
					appUser.setCredit(appUser.getCredit()+delta);
				}

			}

			pm.currentTransaction().commit();

		} finally {
			pm.close();
		}

	}

	static UserProfileData appUserToUserProfileData(AppUser appUser) {

		UserProfileData userData = new UserProfileData();

		HashMap<String, FBFriend> fbFriends = getFriends(appUser.getFBFriends_id(),appUser.getFBFriends_link(),appUser.getFBFriends_name());

		userData.setFB_Friends(fbFriends);
		userData.setNickName(appUser.getNickName());
		userData.setCity(appUser.getCity());
		userData.setCredit(appUser.getCredit());
		userData.setDogName(appUser.getDogName());
		userData.setDogGender(appUser.getDogGender());
		userData.setOKwithFemales(appUser.getOKwithOtherFemales());
		userData.setOKwithMales(appUser.getOKwithOtherMales());
		userData.setHouseNumber(appUser.getHouseNumber());
		userData.setPhone(appUser.getPhone());
		userData.setStreet(appUser.getStreet());
		userData.setId(appUser.getUid());
		userData.setRanking(appUser.getRanking());
		userData.setNumberOfRankers(appUser.getNumberOfRankers());
		userData.setFBprofileLink(appUser.getFBprofileLink());
		userData.setGeoLocation(new Location(appUser.getLat(),appUser.getLng()));
		userData.setEmail(appUser.getEmail());
		userData.setWantDailyEmails(appUser.getWantEmails());
		return userData;


	}

	private static HashMap<String, FBFriend> getFriends(String[] fbFriends_id,	String[] fbFriends_link, String[] fbFriends_name) {
		HashMap<String, FBFriend> res = new HashMap<String, FBFriend>();

		for(int i=0 ; i<fbFriends_id.length ; i++){
			FBFriend friend = new FBFriend();
			friend.setId(fbFriends_id[i]);
			friend.setLink(fbFriends_link[i]);
			friend.setName(fbFriends_name[i]);

			res.put(friend.getId(), friend);


		}

		return res;

	}

	@Override
	public void addAppUser(UserProfileData userData, String accessToken) throws IOException {

		PersistenceManager pm = PMF.getPersistenceManager();
		AppUser newAppUser = new AppUser(userData);

		HashMap<String, FBFriend> friendsMap = userData.getFB_Friends();
		Collection<FBFriend> friends = friendsMap.values();

		ThreeArrays friendsAsArrays = getFriendsAsArrays(friends);
		newAppUser.setFBFriends_id(friendsAsArrays.getIds());
		newAppUser.setFBFriends_name(friendsAsArrays.getNames());
		newAppUser.setFBFriends_link(friendsAsArrays.getLinks());

		try{
			pm.makePersistent(newAppUser);
		}
		finally{
			pm.close();
		}
	}

	AppUser getAppUser(String userId) {
		PersistenceManager pm = PMF.getPersistenceManager();
		List<AppUser> res = new ArrayList<AppUser>();
		try {
			Query q = pm.newQuery(AppUser.class,"uid == id");

			q.declareParameters("String id");
			//	q.setOrdering("walkTime");
			List<AppUser> appUsers = (List<AppUser>) q.execute(userId);

			if(appUsers==null)
				return null;

			if(appUsers.isEmpty()){
				System.out.println("Sdg");
				return null;
			}


			for (AppUser appUser: appUsers) {
				res.add(appUser);

			}
		} finally {
			pm.close();
		}

		return res.get(0);
	}

	public List<UserProfileData> getAllAppUser() {
		PersistenceManager pm = PMF.getPersistenceManager();
		List<UserProfileData> res = new ArrayList<UserProfileData>();
		try {
			Query q = pm.newQuery(AppUser.class);

			//q.declareParameters("String id");
			//	q.setOrdering("walkTime");
			List<AppUser> appUsers = (List<AppUser>) q.execute();

			if(appUsers==null)
				return null;

			if(appUsers.isEmpty()){
				System.out.println("no users");
				return null;
			}

			for (AppUser appUser: appUsers) {
				res.add(appUserToUserProfileData(appUser));

			}
		} finally {
			pm.close();
		}

		return res;
	}

	@Override
	public LoginInfo facebookLogin(String code, String clientId, String loginRedirectURL, String secret) {



//				String token_request_url = "https://graph.facebook.com/oauth/access_token?"
//						+ "client_id="
//						+ clientId
//						+ "&redirect_uri="
//						+ loginRedirectURL
//						+ "&client_secret=" + secret + "&code=" + code;
//		
//		
//				String access_token = null;
//				try {
//					access_token = retrieveAccessToken(token_request_url);
//				} catch (IOException e) {
//					e.printStackTrace();
//				}


		FacebookClient facebookClient = new DefaultFacebookClient(access_token);

		User user = facebookClient.fetchObject("me", User.class);

		LoginInfo loginInfo = new LoginInfo();
		UserProfileData userData;
		if (user != null) {

			FBFriend[] friends = getFbFriends(facebookClient);

			HashMap<String,FBFriend> friendsMap = createMapOfFriends(friends);

			AppUser appUser = getAppUser(user.getId());
			if(appUser==null){
				loginInfo.setIsnewUser(true);

				userData = new UserProfileData();
				userData.setId(user.getId());
				userData.setNickName(user.getName());
				userData.setCity(user.getHometownName());
				userData.setFBprofileLink(user.getLink());
				userData.setEmail(user.getEmail());
				userData.setCredit(3);
			}

			else
			{
				loginInfo.setIsnewUser(false);

				updateUserFBFriendsInDataBase(appUser,friends);

				userData = appUserToUserProfileData(appUser);

			}

			userData.setFB_Friends(friendsMap);

			loginInfo.setUserData(userData);
			loginInfo.setAccessToken(access_token);
			loginInfo.setLoggedIn(true);
			loginInfo.setLogoutUrl(Facebook.getRedirectUri());

		} else {
			loginInfo.setLoggedIn(false);
			loginInfo.setLoginUrl(Facebook.getLoginRedirectURL());
		}
		return loginInfo;

	}

	private ThreeArrays getFriendsAsArrays(Collection<FBFriend> friends) {

		if (friends==null)
			return null;

		ArrayList<String> ids = new ArrayList<String>();
		ArrayList<String> names = new ArrayList<String>();
		ArrayList<String> links = new ArrayList<String>();

		for (FBFriend fbFriend : friends) {
			ids.add(fbFriend.getId());
			names.add(fbFriend.getName());
			links.add(fbFriend.getLink());
		}

		String[] idsArray = (String[]) ids.toArray(new String[0]);
		String[] namesArray = (String[]) names.toArray(new String[0]);
		String[] linksArray = (String[]) links.toArray(new String[0]);

		ThreeArrays res = new ThreeArrays();
		res.setIds(idsArray);
		res.setLinks(linksArray);
		res.setNames(namesArray);

		return res;
	}

	private String[] getLinks(FBFriend[] friends) {

		if (friends==null)
			return null;
		String[] res = new String[friends.length];

		for (int i=0 ; i<res.length ; i++){
			res[i] = friends[i].getLink();
		}

		return res;
	}

	private String[] getIds(FBFriend[] friends) {

		if (friends==null)
			return null;
		String[] res = new String[friends.length];

		for (int i=0 ; i<res.length ; i++){
			res[i] = friends[i].getId();
		}

		return res;
	}

	private String[] getNames(FBFriend[] friends) {

		if (friends==null)
			return null;
		String[] res = new String[friends.length];

		for (int i=0 ; i<res.length ; i++){
			res[i] = friends[i].getName();
		}

		return res;
	}
	private void updateUserFBFriendsInDataBase(AppUser appUser, FBFriend[] friends) {
		PersistenceManager pm = PMF.getPersistenceManager();

		Query q = pm.newQuery(AppUser.class, "uid == id");
		q.declareParameters("String id");
		List<AppUser> appUsers = (List<AppUser>) q.execute(appUser.getUid());

		try {

			for (AppUser user : appUsers) {

				String[] appUserFriendsIDs = user.getFBFriends_id();
				String[] realFriendsIDs = getIds(friends);

				boolean needUpdate = checkIfNeedFriendsUpdate(appUserFriendsIDs,realFriendsIDs);

				if(!needUpdate)
					return;

				pm.currentTransaction().begin();
				user.setFBFriends_id(getIds(friends));
				user.setFBFriends_link(getLinks(friends));
				user.setFBFriends_name(getNames(friends));
				pm.currentTransaction().commit();
			}


		} catch (Exception e) {
			pm.currentTransaction().rollback();
		} finally {
			pm.close();
		}

	}


	private boolean checkIfNeedFriendsUpdate(String[] appUserFriendsIDs,String[] realFriendsIDs) {
		for (String friend : realFriendsIDs) {
			if(!Arrays.asList(appUserFriendsIDs).contains(friend))
				return true;
		}

		return false;
	}

	private FBFriend[] getFbFriends(FacebookClient facebookClient) {
		//Connection<User> myFriends = facebookClient.fetchConnection("me/friends", User.class);


		final Connection<User> myFriends = facebookClient.fetchConnection("me/friends", User.class,Parameter.with("locale", "en_US"), Parameter.with("fields", "id,name, link, gender")); 
		ArrayList<FBFriend> friends = new ArrayList<FBFriend>();

		for (User user : myFriends.getData()) {
			FBFriend friend = new FBFriend();
			friend.setId(user.getId());
			friend.setLink(user.getLink());
			friend.setName(user.getName());

			friends.add(friend);

		}

		return (FBFriend[]) friends.toArray(new FBFriend[0]);


	}

	private HashMap<String, FBFriend> createMapOfFriends(FBFriend[] friends ) {

		HashMap<String, FBFriend> friendsMap = new HashMap<String, FBFriend>();

		for (FBFriend friend : friends) {

			friendsMap.put(friend.getId(), friend);

		}

		return friendsMap;


	}


	private String retrieveAccessToken(String token_request_url) throws IOException {

		URL url = new URL(token_request_url);
		URLConnection url_con = url.openConnection();
		BufferedReader in = new BufferedReader(new InputStreamReader(
				url_con.getInputStream()));
		String answer = in.readLine();
		if(in.readLine()  != null){
			//did not retrieve access token
		}
		in.close();

		//answer = "access_token=ACCESS_TOKEN_GIBRISH&expires=EXPIRES_TIME_MS"
		String access_token = answer.split("&")[0].split("=")[1];	//Finally, I've retrieved the access token !!!
		return access_token;

	}

	@Override
	public double setRanking(String id, double ranking) {

		PersistenceManager pm = PMF.getPersistenceManager();

		double newRanking = 0;
		try {
			Query q = pm.newQuery(AppUser.class, " uid == id");
			q.declareParameters("String id");

			List<AppUser> users = (List<AppUser>) q.execute(id);

			pm.currentTransaction().begin();

			if(users!=null && !users.isEmpty()){

				for (AppUser appUser : users) {
					int rankers = appUser.getNumberOfRankers();
					double currRanking = appUser.getRanking();
					newRanking = (currRanking*rankers+ranking)/(rankers+1);
					appUser.setRanking(newRanking);
					appUser.setNumberOfRankers(rankers+1);
				}
			}

			pm.currentTransaction().commit();

		} finally {
			pm.close();
		}

		return newRanking;

	}

	@Override
	public void updateUserGeoLocation(String id, Location location) {
		PersistenceManager pm = PMF.getPersistenceManager();
		Query q = pm.newQuery(AppUser.class, "uid == id");
		q.declareParameters("String id");
		List<AppUser> appUsers = (List<AppUser>) q.execute(id);

		try {
			pm.currentTransaction().begin();
			for (AppUser appUser : appUsers) {
				appUser.setLat(location.getLat());
				appUser.setLng(location.getLng());
			}
			pm.currentTransaction().commit();

		} catch (Exception e) {
			pm.currentTransaction().rollback();
		} finally {
			pm.close();
		}

	}

	@Override
	public void sendDailyEmail(UserProfileData u) {
		EmailServiceImpl e = new EmailServiceImpl();

		e.IndSendDailyEmail(u);

	}

	@Override
	public void setImage() {


	}

	@Override
	public UserProfileData getUser(String userId) throws NotLoggedInException {
		String [] a = {userId};
		return (getUsers(a))[0];
	}


}